<?php

require_once 'Utils.php';
require_once 'OAuthResponse.php';
require_once 'OAuthServerBase.php';

/**
 * This is class OAuthServer
 *
 * @author: Phong Linh <langxangvn@gmail.com>
*/
class OAuthServer extends OAuthServerBase {	
	
	/**
	 * This is variable _allowedUriSchemes description
	 *
	 */	
	protected $_allowedUriSchemes = array(
		'http',
		'https'
		);

	/**
	 * This is variable _disallowedUriSchemes description
	 *
	 */	
	protected $_disallowedUriSchemes = array(
		'file',
		'callto',
		'mailto'
		);
	
	/**
	 * The data store
	 *
	 * @var OAuthStore 
	 *
	 */
	protected $_forceBypassNonce = true;
	
	/**
	 * This is method handle
	 *
	 * @exception An exception is throw when error
	 * @return OAuthResponse The XML response	
	 *
	 */
	public function handle($action) {
		switch ($action) {			
			case 'requestToken':
				$result = $this->requestToken();
				break;
				
			case 'accessToken':
				$result = $this->accessToken();
				break;
				
			case 'clientAuth':
				$result = $this->clientAuth();
				break;	
			
			case 'authorize':
				$result = $this->authorize();
				break;	
				
			case 'logout':
				$result = $this->logout();
				break;					
				
			default:
				$code = self::$UNSUPPORTED_METHOD;
				$error = $this->getErrorMessage($code);
				throw new OAuthException($error, $code);
		}
		
		return $result;
	}
	
	/**
	 * This is method requestToken
	 *
	 * @return void
	 *
	 */
	public function clientAuth() {		
		//----------------------------------------------------------------------------
		// IF PASSWORD STORE IN DATABASE AS DIGEST FORMAT, CLIENT APPLICATION 
		// MUST GENERATE PASSWORD DIGEST BEFORE BUILD SIGNATURE WITH SAME ALGORITHM
		//----------------------------------------------------------------------------
		$xauthMode = $this->getParam(OAuthRequest::$X_AUTH_MODE, true);
		$xauthPassword = $this->getParam(OAuthRequest::$X_AUTH_PASSWORD, true);
		$xauthUsername = $this->getParam(OAuthRequest::$X_AUTH_USERNAME, true);		
		
		$user = false;
		try {
			$users = new Model_DbTable_Users();
			$user = $users->getUserByEmail($xauthUsername);						
			if($xauthPassword !== $user['password']) {	// check login info
				throw new Exception();	
			}
		} catch(Exception $e) {
			$code = self::$AUTHENTICATE_FAILED;
			$error = $this->getErrorMessage($code);
			throw new OAuthException($error, $code);	
		}
		
		// do verify request if signature not valid
		$request = $this->verify(false, $this->_forceBypassNonce);
		$request['user_id'] = $user['id'];
		return $this->_store->createAccessToken($request);
	}
	
	/**
	 * This is method requestToken
	 *
	 * @return mixed This is the return value description
	 *
	 */
	public function requestToken() {
		$this->verify(false, true);
		
		// optional TTL
		$options = array();
		$ttl     = $this->getParam(self::$XOAUTH_TOKEN_TTL, false);
		if ($ttl) {
			$options['token_ttl'] = $ttl;
		}
		
		// 1.0a Compatibility : associate callback url to the request token
		$callback   = $this->getParam(self::$OAUTH_CALLBACK, true);
		if ($callback) {
			$options['oauth_callback'] = $callback;
		}
		
		$token = false;
		try {
			// Create a request token
			$token  = $this->_store->addRequestToken(
			$this->getParam(self::$OAUTH_CONSUMER_KEY, true), $options);
		} catch(Exception $e) {
			$code = self::$INVALID_TOKEN;
			$error = $this->getErrorMessage($code);
			throw new OAuthException($error, $code);
		}
		
		$result = array();
		$result['oauth_token'] = Rfc3986::urlEncode($token['token']);
		$result['oauth_token_secret'] = Rfc3986::urlEncode($token['token_secret']);		
		$result['oauth_callback_confirmed'] = '1';

		if (!empty($token['token_ttl'])) {			
			$result['xoauth_token_ttl'] = Rfc3986::urlEncode($token['token_ttl']);
		}
		
		// return result
		return $result;
	}
	
	/**
	 * This is method authorizeVerify
	 *
	 * @return mixed This is the return value description
	 *
	 */
	public function authorizeVerify() {
		/* Authorization implementation goes here*/
		$token = $this->getParam(self::$OAUTH_TOKEN, true);
		
		try {
			$rs = $this->_store->getRequestToken($token);			
		} catch(Exception $e) {
			$code = self::$INVALID_TOKEN;
			$error = $this->getErrorMessage($code);
			throw new OAuthException($error, $code);			
		}
		
		// Init session namespace
		$session = new Zend_Session_Namespace(self::$SESSION_NAMESPACE);
		
		// We need to remember the callback		
		if (empty($session->verifyToken) || strcmp($session->verifyToken, $rs['token'])) {
			$session->verifyToken = $rs['token'];
			$session->verifyConsumerKey = $rs['consumer_key'];
			
			$cb = $this->getParam(self::$OAUTH_CALLBACK, true); 
			if ($cb) {
				$session->verifyCallback = $cb;
			} else {
				$session->verifyCallback = $rs['callback_url'];
			}
		}
		
		return $rs;
	}
	
	/**
	 * This is method authorizeFinish
	 *
	 * @param mixed $authorized This is a description
	 * @param mixed $userId This is a description
	 * @return mixed This is the return value description
	 *
	 */
	public function authorizeFinish($authorized, $userId) {
		$token = $this->getParam(self::$OAUTH_TOKEN, true);
		$verifier = null;
		
		// Init session namespace
		$session = new Zend_Session_Namespace(self::$SESSION_NAMESPACE);
		
		// Check session has verified
		if (isset($session->verifyToken) &&	$session->verifyToken == $token) {			
			// Fetch the referrer host from the oauth callback parameter
			$referrer  = '';
			$callback = false;
			if (!empty($session->verifyCallback) && $session->verifyCallback != 'oob') {// OUT OF BAND
				$callback = $session->verifyCallback;
				$components = parse_url($callback);
				if (isset($components['host'])) {
					$referrer = $components['host'];
				}
			}
			
			if ($authorized) {
				// 1.0a Compatibility : create a verifier code
				$verifier = $this->_store->authorizeRequestToken($token, $userId, $referrer);
			} else {
				$this->_store->deleteRequestToken($token);
			}
			
			if (!empty($callback)) {
				$params = array('oauth_token' => rawurlencode($token));
				// 1.0a Compatibility : if verifier code has been generated, add it to the URL
				if ($verifier) {
					$params['oauth_verifier'] = $verifier;
				}
				
				$uri = preg_replace('/\s/', '%20', $callback);
				if (!empty($this->_allowedUriSchemes)) {
					if (!in_array(substr($uri, 0, strpos($uri, '://')), $this->_allowedUriSchemes)) {
						throw new OAuthException('Illegal protocol in redirect URI ' . $uri);
					}
				} else if (!empty($this->_disallowedUriSchemes)) {
					if (in_array(substr($uri, 0, strpos($uri, '://')), $this->_disallowedUriSchemes)) {
						throw new OAuthException('Illegal protocol in redirect URI ' . $uri);
					}
				}
				
				$this->redirect($callback, $params);
			}
		}
		
		return $verifier;
	}	
			
	/**
	 * This is method accessToken
	 *
	 * @param boolean $bypassNonce This is a description
	 * @return mixed This is the return value description
	 *
	 */
	public function accessToken($bypassNonce = false) {		
		$this->verify('request', $bypassNonce);
		
		// optional TTL
		$options = array();
		$ttl     = $this->getParam(self::$XOAUTH_TOKEN_TTL, false);
		if ($ttl) {
			$options['token_ttl'] = $ttl;
		}

		$verifier = $this->getParam(self::$OAUTH_VERIFIER, false);
		if ($verifier) {
			$options['verifier'] = $verifier;
		}
		
		// Create a request token
		$token  = $this->_store->exchangeRequestForAccessToken(
			$this->getParam(self::$OAUTH_TOKEN, true), $options);
		$result = array();
		$result['oauth_token'] = Rfc3986::urlEncode($token['token']);
		$result['oauth_token_secret'] = Rfc3986::urlEncode($token['token_secret']);
		$result['oauth_callback_confirmed'] = 1;

		if (!empty($token['token_ttl'])) {
			$result['xoauth_token_ttl'] = Rfc3986::urlEncode($token['token_ttl']);	
		}
		
		// return result	
		return $result;
	}
}

?>